<!-- Responsibility: Generic bounded split workspace layout for tool pages. -->

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

const props = withDefaults(defineProps<{
  leftCollapsed?: boolean;
  rightOpen?: boolean;
  rightCollapsed?: boolean;
  leftWidth?: number;
  rightWidth?: number;
  minLeftWidth?: number;
  maxLeftWidth?: number;
  minRightWidth?: number;
  maxRightWidth?: number;
  leftCollapsedWidth?: number;
  rightCollapsedWidth?: number;
  leftResizerTestId?: string;
  rightResizerTestId?: string;
  ariaLabel?: string;
}>(), {
  leftCollapsed: false,
  rightOpen: false,
  rightCollapsed: false,
  leftWidth: 30,
  rightWidth: 50,
  minLeftWidth: 18,
  maxLeftWidth: 36,
  minRightWidth: 30,
  maxRightWidth: 70,
  leftCollapsedWidth: 44,
  rightCollapsedWidth: 44,
  leftResizerTestId: undefined,
  rightResizerTestId: undefined,
  ariaLabel: "工作区布局"
});

const emit = defineEmits<{
  "update:leftWidth": [value: number];
  "update:rightWidth": [value: number];
}>();

const layoutRef = ref<HTMLElement | null>(null);
const resizing = ref<"left" | "right" | null>(null);

const clampedLeftWidth = computed(() => clamp(props.leftWidth, props.minLeftWidth, props.maxLeftWidth));
const clampedRightWidth = computed(() => clamp(props.rightWidth, props.minRightWidth, props.maxRightWidth));

const layoutStyle = computed(() => {
  const leftColumn = props.leftCollapsed
    ? `${props.leftCollapsedWidth}px`
    : `minmax(${props.leftCollapsedWidth}px, ${clampedLeftWidth.value}%)`;
  if (!props.rightOpen) {
    return { gridTemplateColumns: `${leftColumn} 6px minmax(0, 1fr)` };
  }
  const mainShare = props.rightCollapsed ? "minmax(0, 1fr)" : `minmax(0, ${100 - clampedRightWidth.value}fr)`;
  const rightResizeColumn = props.rightCollapsed ? "0" : "6px";
  const rightColumn = props.rightCollapsed ? `${props.rightCollapsedWidth}px` : `minmax(280px, ${clampedRightWidth.value}fr)`;
  return { gridTemplateColumns: `${leftColumn} 6px ${mainShare} ${rightResizeColumn} ${rightColumn}` };
});

onBeforeUnmount(() => stopResize());

function startResize(kind: "left" | "right", event: PointerEvent): void {
  if (kind === "left" && props.leftCollapsed) return;
  if (kind === "right" && (!props.rightOpen || props.rightCollapsed)) return;
  event.preventDefault();
  resizing.value = kind;
  window.addEventListener("pointermove", resizePane);
  window.addEventListener("pointerup", stopResize, { once: true });
}

function resizePane(event: PointerEvent): void {
  const rect = layoutRef.value?.getBoundingClientRect();
  if (!rect || rect.width <= 0 || !resizing.value) return;
  if (resizing.value === "left") {
    emit("update:leftWidth", Math.round(clamp(((event.clientX - rect.left) / rect.width) * 100, props.minLeftWidth, props.maxLeftWidth)));
    return;
  }
  const leftPixels = props.leftCollapsed ? props.leftCollapsedWidth : rect.width * (clampedLeftWidth.value / 100);
  const remaining = Math.max(320, rect.width - leftPixels - 18);
  emit("update:rightWidth", Math.round(clamp(((rect.right - event.clientX) / remaining) * 100, props.minRightWidth, props.maxRightWidth)));
}

function stopResize(): void {
  resizing.value = null;
  window.removeEventListener("pointermove", resizePane);
  window.removeEventListener("pointerup", stopResize);
}

function clamp(value: number, min: number, max: number): number {
  if (!Number.isFinite(value)) return min;
  return Math.min(max, Math.max(min, value));
}
</script>

<template>
  <section
    ref="layoutRef"
    class="split-workspace-layout"
    :style="layoutStyle"
    :aria-label="ariaLabel"
    :data-left-collapsed="leftCollapsed"
    :data-right-open="rightOpen"
    :data-right-collapsed="rightCollapsed"
    :data-resizing="resizing || undefined"
  >
    <aside class="split-workspace-pane split-workspace-left" :data-collapsed="leftCollapsed">
      <slot name="left" />
    </aside>
    <div
      class="split-workspace-resizer split-workspace-resizer-left"
      :data-testid="leftResizerTestId"
      role="separator"
      aria-orientation="vertical"
      :aria-valuenow="Math.round(clampedLeftWidth)"
      @pointerdown="startResize('left', $event)"
    />
    <main class="split-workspace-main">
      <slot />
    </main>
    <div
      v-if="rightOpen"
      class="split-workspace-resizer split-workspace-resizer-right"
      :data-testid="rightResizerTestId"
      role="separator"
      aria-orientation="vertical"
      :aria-valuenow="Math.round(clampedRightWidth)"
      @pointerdown="startResize('right', $event)"
    />
    <aside v-if="rightOpen" class="split-workspace-pane split-workspace-right" :data-collapsed="rightCollapsed">
      <slot name="right" />
    </aside>
  </section>
</template>

<style scoped>
.split-workspace-layout {
  display: grid;
  min-width: 0;
  min-height: 0;
  height: 100%;
  overflow: hidden;
  gap: 0;
}

.split-workspace-pane,
.split-workspace-main {
  min-width: 0;
  min-height: 0;
  overflow: hidden;
}

.split-workspace-resizer {
  min-width: 0;
  border-inline: 1px solid transparent;
  border-radius: 999px;
  background: linear-gradient(90deg, transparent 1px, #cbd8d2 2px, #cbd8d2 4px, transparent 5px);
  cursor: col-resize;
}

.split-workspace-resizer-right {
  opacity: 1;
}

.split-workspace-layout[data-left-collapsed="true"] .split-workspace-resizer-left,
.split-workspace-layout[data-right-collapsed="true"] .split-workspace-resizer-right {
  pointer-events: none;
  opacity: 0;
}

.split-workspace-layout[data-resizing] {
  user-select: none;
}

@media (max-width: 720px) {
  .split-workspace-layout {
    display: grid;
    height: 100%;
    min-height: 0;
    grid-template-columns: 1fr !important;
    grid-template-rows: minmax(96px, 28%) minmax(0, 1fr) minmax(120px, 34%);
    gap: 10px;
    overflow: hidden;
  }

  .split-workspace-layout:not([data-right-open="true"]) {
    grid-template-rows: minmax(96px, 30%) minmax(0, 1fr);
  }

  .split-workspace-resizer {
    display: none;
  }
}
</style>
