<template>
  <div class="input-area compact-input-area" ref="inputAreaRoot">
    <div class="stadium-input-wrapper" ref="stadiumShellOuter">
      <div
        class="runtime-queue-list"
        :class="{ 'runtime-queue-list--empty': !runtimeQueuedMessagesForRender.length }"
        ref="runtimeQueueList"
      >
        <div
          v-for="(entry, index) in runtimeQueuedMessagesForRender"
          :key="entry.id"
          class="runtime-queue-item"
          :class="{
            'runtime-queue-item--top': index === 0
          }"
          :data-runtime-queue-id="entry.id || ''"
          :ref="bindRuntimeQueueItemRef(entry.id)"
        >
          <span class="runtime-queue-item__text">{{ entry.text || '' }}</span>
          <button
            type="button"
            class="runtime-queue-item__action"
            @click.stop="$emit('guide-runtime-message', entry.id)"
          >
            引导
          </button>
          <button
            type="button"
            class="runtime-queue-item__action runtime-queue-item__action--danger"
            @click.stop="$emit('delete-runtime-message', entry.id)"
          >
            删除
          </button>
        </div>
      </div>
      <div
        class="stadium-shell"
        ref="compactInputShell"
        :class="{
          'is-multiline': inputIsMultiline,
          'is-focused': inputIsFocused,
          'has-text': (inputMessage || '').trim().length > 0
        }"
      >
        <input
          type="file"
          ref="fileUploadInput"
          class="file-input-hidden"
          multiple
          @change="onFileChange"
        />
        <div class="input-stack">
          <div v-if="selectedImages && selectedImages.length" class="image-inline-row">
            <div class="image-thumbnail-wrapper" v-for="img in selectedImages" :key="img">
              <img :src="getPreviewUrl(img)" :alt="formatImageName(img)" class="image-thumbnail" />
              <button
                type="button"
                class="image-remove-btn-hover"
                @click.stop="$emit('remove-image', img)"
              >
                ×
              </button>
            </div>
          </div>
          <div
            v-if="selectedVideos && selectedVideos.length"
            class="image-inline-row video-inline-row"
          >
            <div class="image-thumbnail-wrapper" v-for="video in selectedVideos" :key="video">
              <img
                :src="getPreviewUrl(video)"
                :alt="formatImageName(video)"
                class="image-thumbnail"
              />
              <button
                type="button"
                class="image-remove-btn-hover"
                @click.stop="$emit('remove-video', video)"
              >
                ×
              </button>
            </div>
          </div>
          <div class="input-row">
            <button
              type="button"
              class="stadium-btn add-btn"
              data-tutorial="quick-menu-open"
              @click.stop="$emit('toggle-quick-menu')"
              :disabled="!isConnected"
            >
              +
            </button>
            <textarea
              :key="composerInputKey"
              ref="stadiumInput"
              class="stadium-input"
              rows="1"
              :value="inputMessage"
              :disabled="!isConnected || inputLocked"
              autocomplete="off"
              autocorrect="off"
              autocapitalize="off"
              placeholder="输入消息... (Ctrl+Enter 发送)"
              @input="onInput"
              @focus="$emit('input-focus')"
              @blur="$emit('input-blur')"
              @keydown.enter.ctrl.prevent="$emit('send-or-stop')"
            ></textarea>
            <button
              type="button"
              class="stadium-btn send-btn"
              @click="$emit('send-or-stop')"
              :disabled="sendButtonDisabled"
            >
              <span v-if="showStopIcon" class="stop-icon"></span>
              <span v-else class="send-icon"></span>
            </button>
          </div>
        </div>
      </div>
      <QuickMenu
        :open="quickMenuOpen"
        :is-connected="isConnected"
        :uploading="uploading"
        :streaming-message="streamingMessage"
        :thinking-mode="thinkingMode"
        :run-mode="runMode"
        :model-menu-open="modelMenuOpen"
        :model-options="modelOptions"
        :current-model-key="currentModelKey"
        :tool-menu-open="toolMenuOpen"
        :tool-settings="toolSettings"
        :tool-settings-loading="toolSettingsLoading"
        :settings-open="settingsOpen"
        :mode-menu-open="modeMenuOpen"
        :compressing="compressing"
        :current-conversation-id="currentConversationId"
        :icon-style="iconStyle"
        :tool-category-icon="toolCategoryIcon"
        :block-upload="blockUpload"
        :block-tool-toggle="blockToolToggle"
        :block-realtime-terminal="blockRealtimeTerminal"
        :block-focus-panel="blockFocusPanel"
        :block-token-panel="blockTokenPanel"
        :block-compress-conversation="blockCompressConversation"
        :block-conversation-review="blockConversationReview"
        :execution-mode-enabled="executionModeEnabled"
        @quick-upload="triggerQuickUpload"
        @pick-images="$emit('pick-images')"
        @pick-video="$emit('pick-video')"
        @toggle-tool-menu="$emit('toggle-tool-menu')"
        @toggle-settings="$emit('toggle-settings')"
        @toggle-mode-menu="$emit('toggle-mode-menu')"
        @select-run-mode="(mode) => $emit('select-run-mode', mode)"
        @toggle-model-menu="$emit('toggle-model-menu')"
        @select-model="(key) => $emit('select-model', key)"
        @update-tool-category="(id, enabled) => $emit('update-tool-category', id, enabled)"
        @realtime-terminal="$emit('realtime-terminal')"
        @toggle-focus-panel="$emit('toggle-focus-panel')"
        @toggle-token-panel="(val) => $emit('toggle-token-panel', val)"
        @compress-conversation="$emit('compress-conversation')"
        @toggle-approval-panel="$emit('toggle-approval-panel')"
        @open-review="$emit('open-review')"
        @open-path-authorization="$emit('open-path-authorization')"
      />
      <div class="permission-switcher" @click.stop>
        <button
          type="button"
          class="permission-switcher__btn"
          :disabled="!isConnected || streamingMessage"
          @click="$emit('open-versioning-dialog')"
        >
          <span>版本控制：{{ versioningEnabled ? '开启' : '关闭' }}</span>
        </button>
        <div class="permission-switcher__block">
          <button
            type="button"
            class="permission-switcher__btn"
            :disabled="!isConnected"
            @click="$emit('toggle-permission-menu')"
          >
            <span>权限：{{ currentPermissionLabel }}</span>
            <span class="permission-switcher__caret" :class="{ open: permissionMenuOpen }">›</span>
          </button>
          <div
            v-if="permissionMenuOpen"
            class="permission-switcher__menu"
            :class="{ 'permission-switcher__menu--split': executionModeEnabled }"
          >
            <div class="permission-switcher__group">
              <div class="permission-switcher__group-title">权限</div>
              <button
                v-for="option in permissionOptions"
                :key="`permission-${option.value}`"
                type="button"
                class="permission-switcher__item"
                :class="{ active: option.value === currentPermissionMode }"
                @click="$emit('change-permission-mode', option.value)"
              >
                <span class="permission-switcher__item-label">{{ option.label }}</span>
                <span class="permission-switcher__item-desc">{{ option.description }}</span>
              </button>
            </div>
            <div v-if="executionModeEnabled" class="permission-switcher__group">
              <div class="permission-switcher__group-title">执行环境</div>
              <button
                v-for="option in executionModeOptions"
                :key="`execution-${option.value}`"
                type="button"
                class="permission-switcher__item"
                :class="{ active: option.value === currentExecutionMode }"
                @click="$emit('change-execution-mode', option.value)"
              >
                <span
                  class="permission-switcher__item-label"
                  :class="{ 'permission-switcher__item-label--warn': option.value === 'direct' }"
                  >{{ option.label }}</span
                >
                <span class="permission-switcher__item-desc">{{ option.description }}</span>
              </button>
            </div>
          </div>
        </div>
      </div>
      <div class="context-usage-switcher" @click.stop>
        <button
          type="button"
          class="context-usage-switcher__btn"
          :disabled="!isConnected"
          @click="$emit('toggle-token-panel')"
        >
          <span
            class="context-usage-ring"
            :style="{
              '--context-progress': `${contextUsagePercent}%`,
              '--context-ring-color': contextUsageColor
            }"
            aria-hidden="true"
          >
            <span class="context-usage-ring__inner"></span>
          </span>
        </button>
        <div class="context-usage-switcher__tooltip">
          <div>{{ contextUsagePercentLabel }}已用</div>
          <div>{{ contextUsageCompactText }}</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import {
  onMounted,
  onBeforeUnmount,
  onBeforeUpdate,
  onUpdated,
  ref,
  watch,
  nextTick,
  computed
} from 'vue';
import QuickMenu from '@/components/input/QuickMenu.vue';
import { useInputStore } from '@/stores/input';
import { usePersonalizationStore } from '@/stores/personalization';

defineOptions({ name: 'InputComposer' });

const emit = defineEmits([
  'update:input-message',
  'input-change',
  'input-focus',
  'input-blur',
  'toggle-quick-menu',
  'send-message',
  'send-or-stop',
  'quick-upload',
  'pick-images',
  'pick-video',
  'toggle-tool-menu',
  'toggle-mode-menu',
  'toggle-model-menu',
  'select-run-mode',
  'select-model',
  'toggle-settings',
  'update-tool-category',
  'realtime-terminal',
  'toggle-focus-panel',
  'toggle-token-panel',
  'compress-conversation',
  'toggle-approval-panel',
  'file-selected',
  'remove-image',
  'remove-video',
  'open-review',
  'open-path-authorization',
  'toggle-permission-menu',
  'change-permission-mode',
  'change-execution-mode',
  'open-versioning-dialog',
  'guide-runtime-message',
  'delete-runtime-message',
  'composer-height-change'
]);

const props = defineProps<{
  inputMessage: string;
  inputIsMultiline: boolean;
  inputIsFocused: boolean;
  isConnected: boolean;
  streamingMessage: boolean;
  inputLocked: boolean;
  uploading: boolean;
  thinkingMode: boolean;
  runMode: 'fast' | 'thinking' | 'deep';
  quickMenuOpen: boolean;
  toolMenuOpen: boolean;
  modeMenuOpen: boolean;
  modelMenuOpen: boolean;
  toolSettings: Array<{ id: string; label: string; enabled: boolean }>;
  toolSettingsLoading: boolean;
  settingsOpen: boolean;
  compressing: boolean;
  currentConversationId: string | null;
  iconStyle: (key: string) => Record<string, string>;
  toolCategoryIcon: (categoryId: string) => string;
  modelOptions: Array<{
    key: string;
    label: string;
    description: string;
    disabled?: boolean;
    supportsImage?: boolean;
    supportsVideo?: boolean;
    contextWindow?: number | null;
  }>;
  currentModelKey: string;
  selectedImages?: string[];
  selectedVideos?: string[];
  mediaUploading?: boolean;
  blockUpload?: boolean;
  blockToolToggle?: boolean;
  blockRealtimeTerminal?: boolean;
  blockFocusPanel?: boolean;
  blockTokenPanel?: boolean;
  blockCompressConversation?: boolean;
  blockConversationReview?: boolean;
  currentPermissionMode: 'readonly' | 'approval' | 'auto_approval' | 'unrestricted';
  permissionMenuOpen: boolean;
  permissionOptions: Array<{ value: string; label: string; description: string }>;
  executionModeEnabled?: boolean;
  currentExecutionMode?: 'sandbox' | 'direct';
  executionModeOptions?: Array<{ value: string; label: string; description: string }>;
  currentContextTokens: number;
  versioningEnabled?: boolean;
  runtimeQueuedMessages?: Array<{ id: string; text: string }>;
}>();

const inputStore = useInputStore();
const personalizationStore = usePersonalizationStore();
const inputAreaRoot = ref<HTMLElement | null>(null);
const stadiumShellOuter = ref<HTMLElement | null>(null);
const compactInputShell = ref<HTMLElement | null>(null);
const stadiumInput = ref<HTMLTextAreaElement | null>(null);
const fileUploadInput = ref<HTMLInputElement | null>(null);
const baselineComposerVisualHeight = ref<number>(0);
let composerResizeObserver: ResizeObserver | null = null;

const formatImageName = (path: string): string => {
  if (!path) return '';
  const parts = path.split(/[/\\]/);
  return parts[parts.length - 1] || path;
};

const getPreviewUrl = (path: string): string => {
  if (!path) return '';
  return `/api/gui/files/download?path=${encodeURIComponent(path)}`;
};

const applyLineMetrics = (lines: number, multiline: boolean) => {
  inputStore.setInputLineCount(lines);
  inputStore.setInputMultiline(multiline);
};

const composerInputKey = computed(
  () => `${props.currentConversationId || 'new'}:${props.isConnected ? 'online' : 'offline'}`
);

const runtimeQueuedMessagesForRender = computed(() => {
  const list = Array.isArray(props.runtimeQueuedMessages) ? props.runtimeQueuedMessages : [];
  return list.filter((item) => item && item.id && item.text);
});

const RUNTIME_QUEUE_ANIM_DURATION = 300;
const RUNTIME_QUEUE_ANIM_EASING = 'cubic-bezier(0.25, 0.8, 0.25, 1)';
const runtimeQueueList = ref<HTMLElement | null>(null);
const runtimeQueueItemRefs = new Map<string, HTMLElement>();
const runtimeQueuePrevIds: string[] = [];
const runtimeQueueRenderedIds: string[] = [];
const runtimeQueuePrevRects = new Map<string, DOMRect>();
const runtimeQueuePrevElements = new Map<string, HTMLElement>();
const runtimeQueueGhosts = new Map<string, HTMLElement>();
const runtimeQueueActiveAnimations = new WeakMap<HTMLElement, Animation>();
const runtimeQueueFallbackTimers = new WeakMap<HTMLElement, number>();
let runtimeQueueAnimationReady = false;

const bindRuntimeQueueItemRef = (id: string) => (el: Element | null) => {
  if (!id) {
    return;
  }
  if (el instanceof HTMLElement) {
    runtimeQueueItemRefs.set(id, el);
  } else {
    runtimeQueueItemRefs.delete(id);
  }
};

const stopRuntimeQueueAnimation = (el: HTMLElement) => {
  const anim = runtimeQueueActiveAnimations.get(el);
  if (anim) {
    try {
      anim.cancel();
    } catch {
      // ignore
    }
    runtimeQueueActiveAnimations.delete(el);
  }
  const timer = runtimeQueueFallbackTimers.get(el);
  if (timer) {
    clearTimeout(timer);
    runtimeQueueFallbackTimers.delete(el);
  }
  el.style.removeProperty('transition');
  el.style.removeProperty('transform');
};

const playRuntimeQueueTransform = (
  el: HTMLElement,
  fromY: number,
  toY: number,
  done?: () => void
) => {
  if (Math.abs(fromY - toY) < 0.5) {
    stopRuntimeQueueAnimation(el);
    el.style.removeProperty('transition');
    el.style.removeProperty('transform');
    done?.();
    return;
  }
  stopRuntimeQueueAnimation(el);
  const from = `translateY(${fromY}px)`;
  const to = `translateY(${toY}px)`;
  const finish = () => {
    el.style.removeProperty('transition');
    el.style.removeProperty('transform');
    done?.();
  };

  el.style.transition = 'none';
  el.style.transform = from;

  if (typeof el.animate === 'function') {
    // 强制应用起始位移，避免首帧先闪现到终点
    void el.offsetHeight;
    const animation = el.animate([{ transform: from }, { transform: to }], {
      duration: RUNTIME_QUEUE_ANIM_DURATION,
      easing: RUNTIME_QUEUE_ANIM_EASING,
      fill: 'both'
    });
    runtimeQueueActiveAnimations.set(el, animation);
    animation.addEventListener(
      'finish',
      () => {
        if (runtimeQueueActiveAnimations.get(el) === animation) {
          runtimeQueueActiveAnimations.delete(el);
        }
        finish();
      },
      { once: true }
    );
    animation.addEventListener(
      'cancel',
      () => {
        if (runtimeQueueActiveAnimations.get(el) === animation) {
          runtimeQueueActiveAnimations.delete(el);
        }
      },
      { once: true }
    );
    return;
  }

  requestAnimationFrame(() => {
    el.style.transition = `transform ${RUNTIME_QUEUE_ANIM_DURATION}ms ${RUNTIME_QUEUE_ANIM_EASING}`;
    el.style.transform = to;
    const timer = window.setTimeout(() => {
      runtimeQueueFallbackTimers.delete(el);
      finish();
    }, RUNTIME_QUEUE_ANIM_DURATION + 24);
    runtimeQueueFallbackTimers.set(el, timer);
  });
};

const resolveRuntimeQueueOffset = (el: HTMLElement): number => {
  const rectHeight = Math.round(el.getBoundingClientRect().height);
  if (rectHeight > 0) {
    return rectHeight;
  }
  if (el.offsetHeight > 0) {
    return el.offsetHeight;
  }
  return 34;
};

const snapshotRuntimeQueueIds = (): string[] => {
  return runtimeQueuedMessagesForRender.value
    .map((entry) => String(entry?.id || '').trim())
    .filter((id) => !!id);
};

const spawnRuntimeQueueLeaveGhost = (id: string) => {
  const sourceEl = runtimeQueuePrevElements.get(id);
  const sourceRect = runtimeQueuePrevRects.get(id);
  const listEl = runtimeQueueList.value;
  if (!sourceEl || !sourceRect || !listEl || !listEl.isConnected) {
    return;
  }

  const existingGhost = runtimeQueueGhosts.get(id);
  if (existingGhost) {
    stopRuntimeQueueAnimation(existingGhost);
    existingGhost.remove();
    runtimeQueueGhosts.delete(id);
  }

  const ghost = sourceEl.cloneNode(true) as HTMLElement;
  const listRect = listEl.getBoundingClientRect();
  const top = sourceRect.top - listRect.top;
  const width = sourceRect.width > 0 ? sourceRect.width : listRect.width;

  ghost.style.position = 'absolute';
  ghost.style.left = '0';
  ghost.style.top = `${Math.round(top)}px`;
  ghost.style.width = `${Math.round(width)}px`;
  ghost.style.pointerEvents = 'none';
  ghost.style.zIndex = '0';
  ghost.style.margin = '0';
  ghost.querySelectorAll('button').forEach((button) => {
    if (button instanceof HTMLButtonElement) {
      button.disabled = true;
    }
  });

  listEl.appendChild(ghost);
  runtimeQueueGhosts.set(id, ghost);

  const offset = resolveRuntimeQueueOffset(ghost);
  playRuntimeQueueTransform(ghost, 0, offset, () => {
    runtimeQueueGhosts.delete(id);
    ghost.remove();
  });
};

const animateRuntimeQueueEnter = (el: HTMLElement) => {
  const offset = resolveRuntimeQueueOffset(el);
  el.style.pointerEvents = 'none';
  playRuntimeQueueTransform(el, offset, 0, () => {
    el.style.removeProperty('pointer-events');
  });
};

onBeforeUpdate(() => {
  runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length, ...runtimeQueueRenderedIds);
  runtimeQueuePrevRects.clear();
  runtimeQueuePrevElements.clear();

  runtimeQueueRenderedIds.forEach((id) => {
    const el = runtimeQueueItemRefs.get(id);
    if (!el?.isConnected) {
      return;
    }
    runtimeQueuePrevRects.set(id, el.getBoundingClientRect());
    runtimeQueuePrevElements.set(id, el);
  });
});

onUpdated(() => {
  const currentIds = snapshotRuntimeQueueIds();
  if (!runtimeQueueAnimationReady) {
    runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length, ...currentIds);
    runtimeQueueAnimationReady = true;
    runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
    runtimeQueuePrevRects.clear();
    runtimeQueuePrevElements.clear();
    return;
  }

  const previousIdSet = new Set(runtimeQueuePrevIds);
  const currentIdSet = new Set(currentIds);
  const enteringIdSet = new Set(currentIds.filter((id) => !previousIdSet.has(id)));
  const leavingIds = runtimeQueuePrevIds.filter((id) => !currentIdSet.has(id));

  leavingIds.forEach((id) => {
    spawnRuntimeQueueLeaveGhost(id);
    runtimeQueueItemRefs.delete(id);
  });

  currentIds.forEach((id) => {
    const el = runtimeQueueItemRefs.get(id);
    if (!el?.isConnected) {
      return;
    }
    const prevRect = runtimeQueuePrevRects.get(id);
    if (!prevRect || enteringIdSet.has(id)) {
      animateRuntimeQueueEnter(el);
      return;
    }
    const nextRect = el.getBoundingClientRect();
    const deltaY = prevRect.top - nextRect.top;
    if (Math.abs(deltaY) < 0.5) {
      return;
    }
    playRuntimeQueueTransform(el, deltaY, 0);
  });

  runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length, ...currentIds);
  runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
  runtimeQueuePrevRects.clear();
  runtimeQueuePrevElements.clear();
});

const hasComposerContent = computed(() => {
  const hasText = !!(props.inputMessage || '').trim();
  if (props.streamingMessage) {
    return hasText;
  }
  const hasImages = Array.isArray(props.selectedImages) && props.selectedImages.length > 0;
  const hasVideos = Array.isArray(props.selectedVideos) && props.selectedVideos.length > 0;
  return hasText || hasImages || hasVideos;
});

const showStopIcon = computed(() => props.streamingMessage && !hasComposerContent.value);

const sendButtonDisabled = computed(() => {
  if (!props.isConnected) {
    return true;
  }
  if (props.streamingMessage) {
    return false;
  }
  if (props.inputLocked) {
    return true;
  }
  if (props.mediaUploading) {
    return true;
  }
  return !hasComposerContent.value;
});

const hasRuntimeLayoutExpansion = computed(() => {
  const hasQueue = runtimeQueuedMessagesForRender.value.length > 0;
  const hasImages = Array.isArray(props.selectedImages) && props.selectedImages.length > 0;
  const hasVideos = Array.isArray(props.selectedVideos) && props.selectedVideos.length > 0;
  return hasQueue || hasImages || hasVideos || !!props.inputIsMultiline;
});

const collectComposerVisualHeight = () => {
  const root = inputAreaRoot.value;
  const shell = compactInputShell.value;
  if (!root || !shell) {
    return 0;
  }
  const shellRect = shell.getBoundingClientRect();
  let top = shellRect.top;
  let bottom = shellRect.bottom;
  const nodes = root.querySelectorAll('.runtime-queue-list:not(.runtime-queue-list--empty)');
  nodes.forEach((node) => {
    if (!(node instanceof HTMLElement)) return;
    const rect = node.getBoundingClientRect();
    top = Math.min(top, rect.top);
    bottom = Math.max(bottom, rect.bottom);
  });
  return Math.max(0, bottom - top);
};

const emitComposerHeight = () => {
  const visualHeight = collectComposerVisualHeight();
  if (!visualHeight) {
    return;
  }
  if (baselineComposerVisualHeight.value <= 0 || !hasRuntimeLayoutExpansion.value) {
    baselineComposerVisualHeight.value = visualHeight;
  }
  const baseline = baselineComposerVisualHeight.value || visualHeight;
  const growth = Math.max(0, visualHeight - baseline);
  const baseReservedHeight = 80;
  const reservedHeight = Math.ceil(baseReservedHeight + growth);
  emit('composer-height-change', {
    reservedHeight,
    visualHeight,
    growth
  });
};

const adjustTextareaSize = () => {
  const textarea = stadiumInput.value;
  if (!textarea) {
    return;
  }
  textarea.style.height = 'auto';
  const computedStyle = window.getComputedStyle(textarea);
  const lineHeight = parseFloat(computedStyle.lineHeight || '20') || 20;
  const maxHeight = lineHeight * 6;
  const targetHeight = Math.min(textarea.scrollHeight, maxHeight);
  const lines = Math.max(1, Math.round(targetHeight / lineHeight));
  const multiline = targetHeight > lineHeight * 1.4;
  applyLineMetrics(lines, multiline);
  textarea.style.height = `${targetHeight}px`;
  nextTick(() => {
    emitComposerHeight();
  });
};

const onInput = (event: Event) => {
  const target = event.target as HTMLTextAreaElement;
  if (!props.isConnected || props.inputLocked) {
    target.value = props.inputMessage || '';
    return;
  }
  emit('update:input-message', target.value);
  emit('input-change');
  adjustTextareaSize();
};

const onFileChange = (event: Event) => {
  const target = event.target as HTMLInputElement;
  emit('file-selected', target?.files || null);
  if (target) {
    target.value = '';
  }
};

const triggerQuickUpload = () => {
  if (!props.isConnected || props.uploading) {
    return;
  }
  emit('quick-upload');
};

const currentPermissionLabel = computed(() => {
  const matched = (props.permissionOptions || []).find(
    (item) => item.value === props.currentPermissionMode
  );
  return matched ? matched.label : props.currentPermissionMode;
});

const currentExecutionLabel = computed(() => {
  const options = props.executionModeOptions || [];
  const currentMode = String(props.currentExecutionMode || '');
  const currentMatched = options.find((item) => item.value === currentMode);
  return currentMatched ? currentMatched.label : currentMode;
});

const modelContextWindow = computed(() => {
  const list = Array.isArray(props.modelOptions) ? props.modelOptions : [];
  const current = list.find((item) => item.key === props.currentModelKey);
  return Number(current?.contextWindow || 0);
});

const autoDeepCompressEnabled = computed(() => {
  return !!personalizationStore?.form?.auto_deep_compress_enabled;
});

const deepCompressLimit = computed(() => {
  const custom = Number(personalizationStore?.form?.deep_compress_trigger_tokens || 0);
  if (custom > 0) return custom;
  return 150000;
});

const contextUsageLimit = computed(() => {
  if (autoDeepCompressEnabled.value) {
    return deepCompressLimit.value;
  }
  if (modelContextWindow.value > 0) {
    return modelContextWindow.value;
  }
  return 0;
});

const contextUsagePercent = computed(() => {
  const limit = Number(contextUsageLimit.value || 0);
  if (limit <= 0) return 0;
  const current = Math.max(0, Number(props.currentContextTokens || 0));
  return Math.max(0, Math.min(100, (current / limit) * 100));
});

const contextUsagePercentLabel = computed(() => `${Math.round(contextUsagePercent.value)}%`);

const contextUsageColor = computed(() => {
  const percent = contextUsagePercent.value;
  if (percent < 40) return '#16a34a';
  if (percent <= 80) return '#f59e0b';
  return '#ef4444';
});

const formatCompactTokens = (value: number) => {
  const num = Math.max(0, Number(value || 0));
  if (num >= 1000) {
    const raw = num / 1000;
    const text = raw >= 100 ? Math.round(raw).toString() : raw.toFixed(1).replace(/\.0$/, '');
    return `${text}k`;
  }
  return `${Math.round(num)}`;
};

const contextUsageCompactText = computed(() => {
  const current = formatCompactTokens(props.currentContextTokens || 0);
  const limit = Number(contextUsageLimit.value || 0);
  if (limit <= 0) return `${current}/--`;
  return `${current}/${formatCompactTokens(limit)}`;
});

defineExpose({
  inputAreaRoot,
  stadiumShellOuter,
  compactInputShell,
  stadiumInput,
  fileUploadInput
});

watch(
  () => props.inputMessage,
  async () => {
    await nextTick();
    adjustTextareaSize();
  }
);

watch(
  () => props.isConnected,
  async (connected) => {
    if (!connected && stadiumInput.value) {
      stadiumInput.value.blur();
    }
    await nextTick();
    adjustTextareaSize();
  }
);

watch(
  () => props.currentConversationId,
  async () => {
    baselineComposerVisualHeight.value = 0;
    await nextTick();
    emitComposerHeight();
  }
);

watch(
  () => props.runtimeQueuedMessages,
  async () => {
    await nextTick();
    emitComposerHeight();
  },
  { deep: true }
);

watch(
  () => [props.selectedImages?.length || 0, props.selectedVideos?.length || 0],
  async () => {
    await nextTick();
    emitComposerHeight();
  }
);

onMounted(() => {
  adjustTextareaSize();
  nextTick(() => {
    emitComposerHeight();
    if (typeof ResizeObserver !== 'undefined') {
      composerResizeObserver = new ResizeObserver(() => {
        emitComposerHeight();
      });
      if (inputAreaRoot.value) {
        composerResizeObserver.observe(inputAreaRoot.value);
      }
      if (stadiumShellOuter.value) {
        composerResizeObserver.observe(stadiumShellOuter.value);
      }
      if (compactInputShell.value) {
        composerResizeObserver.observe(compactInputShell.value);
      }
    }
  });
});

onBeforeUnmount(() => {
  runtimeQueueItemRefs.forEach((el) => {
    if (el) {
      stopRuntimeQueueAnimation(el);
    }
  });
  runtimeQueueGhosts.forEach((ghost) => {
    stopRuntimeQueueAnimation(ghost);
    ghost.remove();
  });
  runtimeQueueGhosts.clear();
  runtimeQueueItemRefs.clear();
  runtimeQueuePrevIds.splice(0, runtimeQueuePrevIds.length);
  runtimeQueueRenderedIds.splice(0, runtimeQueueRenderedIds.length);
  runtimeQueuePrevRects.clear();
  runtimeQueuePrevElements.clear();
  runtimeQueueAnimationReady = false;
  if (composerResizeObserver) {
    composerResizeObserver.disconnect();
    composerResizeObserver = null;
  }
});
</script>

<style scoped>
.image-inline-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  padding: 4px 10px 2px;
  line-height: 1.4;
}

.image-thumbnail-wrapper {
  position: relative;
  width: 60px;
  height: 60px;
  border-radius: 6px;
  overflow: hidden;
  cursor: pointer;
  border: 1px solid var(--border-color, #2a2f3a);
}

.image-thumbnail {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.image-remove-btn-hover {
  position: absolute;
  top: 0;
  right: 0;
  background: none;
  color: #fff;
  border: none;
  font-size: 20px;
  font-weight: bold;
  line-height: 1;
  cursor: pointer;
  display: none;
  padding: 2px 4px;
  text-shadow: 0 0 3px rgba(0, 0, 0, 0.8);
  transition: color 0.15s ease;
}

.image-thumbnail-wrapper:hover .image-remove-btn-hover {
  display: block;
}

.image-remove-btn-hover:hover {
  color: #ef4444;
}
</style>
