<script lang="ts">
  import type { ViewerSegment } from "$lib/types/app";
  import { measureTextHeight } from "$lib/utils/textMeasure.js";
  import { buildCumulativeOffsets, findVisibleRange } from "$lib/utils/virtualList.js";
  import { getPreferences } from "$lib/stores/preferences.svelte.js";
  import { bodyPretextLineHeight, pretextFontShorthand } from "$lib/utils/accessibilityTypography.js";

  // Props
  let {
    segments = [],
    activeSegmentIdx = -1,
    matchingSegments = new Set<number>(),
    showTimestamps = true,
    editingIdx = -1,
    editingText = $bindable(""),
    searchQuery = "",
    formatTime = (t: number) => String(t),
    onSeekToTime = (_time: number) => {},
    onStartEditing = (_idx: number) => {},
    onFinishEditing = () => {},
    onToggleStar = (_idx: number) => {},
    onCopySegment = (_idx: number) => {},
    onDeleteSegment = (_idx: number) => {},
    highlightText = (t: string) => t,
  } = $props<{
    segments?: ViewerSegment[];
    activeSegmentIdx?: number;
    matchingSegments?: Set<number>;
    showTimestamps?: boolean;
    editingIdx?: number;
    editingText?: string;
    searchQuery?: string;
    formatTime?: (time: number) => string;
    onSeekToTime?: (time: number) => void;
    onStartEditing?: (idx: number) => void;
    onFinishEditing?: () => void;
    onToggleStar?: (idx: number) => void;
    onCopySegment?: (idx: number) => void;
    onDeleteSegment?: (idx: number) => void;
    highlightText?: (text: string) => string;
  }>();

  // Internal state
  let containerEl = $state<HTMLDivElement | null>(null);
  let containerHeight = $state(0);
  let containerWidth = $state(0);
  let scrollTop = $state(0);
  let hasSelection = $state(false);
  const prefs = getPreferences();

  // Segment height measurement
  const SEGMENT_PADDING = 20;  // py-2 (16px) + gap-1 (4px)
  const BUFFER_COUNT = 5;
  const SELECTION_BUFFER = 10;
  let textFont = $derived(pretextFontShorthand(prefs.accessibility, 13));
  let textLineHeight = $derived(bodyPretextLineHeight(prefs.accessibility, 13));

  // Pre-calculate heights for all segments
  let segmentHeights = $derived.by(() => {
    if (!segments.length || containerWidth <= 0) return [];
    // Available width for text: container - star (26px) - timestamp (48px if shown) - padding (24px) - actions (0, hidden)
    const timestampWidth = showTimestamps ? 48 : 0;
    const textWidth = containerWidth - 26 - timestampWidth - 24;
    if (textWidth <= 0) return segments.map(() => 40);

    return segments.map((seg: ViewerSegment) => {
      const text = seg.text?.trim() || "";
      if (!text) return SEGMENT_PADDING + textLineHeight;
      const { height } = measureTextHeight(text, textFont, textWidth, textLineHeight);
      return height + SEGMENT_PADDING;
    });
  });

  // Cumulative offsets for O(1) lookup
  let cumulativeOffsets = $derived(buildCumulativeOffsets(segmentHeights));

  let totalHeight = $derived(
    cumulativeOffsets.length > 0 ? cumulativeOffsets[cumulativeOffsets.length - 1] : 0
  );

  // Determine visible range
  let visibleRange = $derived.by(() => {
    const buffer = hasSelection ? SELECTION_BUFFER : BUFFER_COUNT;
    return findVisibleRange(
      cumulativeOffsets,
      segments.length,
      scrollTop,
      containerHeight,
      buffer,
    );
  });

  // Visible segment slice with positioning
  let visibleItems = $derived.by(() => {
    const items: Array<{ seg: ViewerSegment; idx: number; top: number; height: number }> = [];
    for (let i = visibleRange.start; i < visibleRange.end; i++) {
      items.push({
        seg: segments[i],
        idx: segments[i]._idx,
        top: cumulativeOffsets[i],
        height: segmentHeights[i],
      });
    }
    return items;
  });

  // Scroll to a specific segment index (used for active segment tracking + search)
  export function scrollToSegment(idx: number) {
    if (!containerEl || idx < 0 || idx >= segments.length) return;
    const segTop = cumulativeOffsets[idx];
    const segHeight = segmentHeights[idx];
    const viewCenter = containerHeight / 2;
    containerEl.scrollTop = segTop - viewCenter + segHeight / 2;
  }

  function onScroll(e: Event) {
    scrollTop = (e.currentTarget as HTMLDivElement).scrollTop;
    // Track selection state for expanded buffer
    hasSelection = !document.getSelection()?.isCollapsed;
  }

  // ResizeObserver for container dimensions
  $effect(() => {
    if (!containerEl) return;
    const ro = new ResizeObserver((entries) => {
      for (const entry of entries) {
        containerHeight = entry.contentRect.height;
        containerWidth = entry.contentRect.width;
      }
    });
    ro.observe(containerEl);
    return () => ro.disconnect();
  });

  // Auto-scroll to active segment during playback
  $effect(() => {
    if (activeSegmentIdx >= 0) {
      // Find the index in our segments array that matches
      const arrayIdx = segments.findIndex((s: ViewerSegment) => s._idx === activeSegmentIdx);
      if (arrayIdx >= 0) {
        const segTop = cumulativeOffsets[arrayIdx];
        const segBottom = segTop + segmentHeights[arrayIdx];
        // Only scroll if the active segment is out of view
        if (segTop < scrollTop || segBottom > scrollTop + containerHeight) {
          scrollToSegment(arrayIdx);
        }
      }
    }
  });
</script>

<div
  bind:this={containerEl}
  class="flex-1 overflow-y-auto px-5 py-3 min-h-0"
  onscroll={onScroll}
>
  <div class="relative" style="height: {totalHeight}px">
    {#each visibleItems as { seg, idx, top, height } (idx)}
      <div
        class="absolute left-0 right-0 group flex gap-3 px-3 py-2 rounded-lg cursor-pointer transition-colors
          {activeSegmentIdx === idx
            ? 'bg-accent/10'
            : matchingSegments.has(idx)
              ? 'bg-warning/10'
              : 'hover:bg-hover'}"
        style="top: {top}px"
        onclick={() => onSeekToTime(seg.start)}
        ondblclick={() => onStartEditing(idx)}
        role="button"
        tabindex="0"
        onkeydown={(e) => { if (e.key === "Enter" && editingIdx !== idx) onSeekToTime(seg.start); }}
      >
        <!-- Star -->
        <button
          class="mt-0.5 flex-shrink-0 {seg.starred ? 'text-accent' : 'text-text-tertiary opacity-0 group-hover:opacity-100'}"
          onclick={(e) => { e.stopPropagation(); onToggleStar(idx); }}
          aria-label={seg.starred ? "Unstar" : "Star"}
        >
          <svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill={seg.starred ? "currentColor" : "none"} stroke="currentColor" stroke-width="2">
            <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
          </svg>
        </button>
        <!-- Timestamp -->
        {#if showTimestamps}
          <span class="text-[12px] text-text-secondary tabular-nums flex-shrink-0 mt-0.5 min-w-[36px]">
            {formatTime(seg.start)}
          </span>
        {/if}
        <!-- Text (editable or display) -->
        {#if editingIdx === idx}
          <textarea
            class="flex-1 bg-bg-input border border-accent rounded-lg px-2 py-1 text-[13px] text-text
                   resize-none focus:outline-none min-h-[40px]"
            style="line-height: {textLineHeight}px"
            bind:value={editingText}
            onkeydown={(e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); onFinishEditing(); } if (e.key === "Escape") { onFinishEditing(); } }}
            onblur={onFinishEditing}
            onclick={(e) => e.stopPropagation()}
            data-no-transition
          ></textarea>
        {:else}
          <p class="text-[13px] text-text flex-1" style="line-height: {textLineHeight}px">
            {#if searchQuery.trim()}
              {@html highlightText(seg.text.trim())}
            {:else}
              {seg.text.trim()}
            {/if}
          </p>
        {/if}
        <!-- Actions (hover reveal) -->
        <div class="flex items-center gap-1 opacity-0 group-hover:opacity-100 flex-shrink-0">
          <button
            class="text-text-tertiary hover:text-accent"
            onclick={(e) => { e.stopPropagation(); onCopySegment(idx); }}
            title="Copy segment"
          >
            <svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <rect x="9" y="9" width="13" height="13" rx="2" /><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
            </svg>
          </button>
          <button
            class="text-text-tertiary hover:text-danger"
            onclick={(e) => { e.stopPropagation(); onDeleteSegment(idx); }}
            title="Delete segment"
          >
            <svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
              <path d="M18 6L6 18M6 6l12 12" stroke-linecap="round" />
            </svg>
          </button>
        </div>
      </div>
    {/each}
  </div>
</div>
