import { computed, type Ref } from 'vue'
import { scaleTime } from 'd3-scale'
import { useUiStore } from '@/stores/uiStore'
import { ROW_HEIGHT, AXIS_HEIGHT, BOTTOM_PADDING, type TimelineItem } from '@/types/timeline'

/**
 * Composable for managing timeline layout, scaling, and dimensions
 */
export function useTimelineLayout(
  data: Ref<TimelineItem[]>,
  width: Ref<number>,
  height: Ref<number>
) {
  const uiStore = useUiStore()

  function resolveStart(item: TimelineItem): Date | undefined {
    return item.start ?? item.timestamp ?? item.end
  }

  function resolveEnd(item: TimelineItem): Date | undefined {
    return item.end ?? item.start ?? item.timestamp
  }

  // calculate total height to ensure the grid fills at least the viewport height
  const totalHeight = computed(() =>
    Math.max(data.value.length * ROW_HEIGHT + AXIS_HEIGHT + BOTTOM_PADDING, height.value),
  )

  // dynamic width based on zoom level
  const zoomedWidth = computed(() => width.value * uiStore.zoomLevel)

  /**
   * D3 Time Scale
   * Maps the temporal domain (start time to end time) to the horizontal range (0 to width).
   * Includes a 5% padding on both ends for better visibility.
   */
  const timeScale = computed(() => {
    if (width.value === 0 || data.value.length === 0) return null

    const resolvedRanges = data.value
      .map((item) => {
        const start = resolveStart(item)
        const end = resolveEnd(item)
        if (!start || !end) return null
        return { start, end }
      })
      .filter((item): item is { start: Date; end: Date } => item !== null)

    if (resolvedRanges.length === 0) return null

    // calculate extent of the data
    const minStart = new Date(Math.min(...resolvedRanges.map((d) => d.start.getTime())))
    const maxEnd = new Date(Math.max(...resolvedRanges.map((d) => d.end.getTime())))

    // add 5% padding for visual comfort
    const duration = maxEnd.getTime() - minStart.getTime()
    const padding = duration * 0.05
    const domain = [new Date(minStart.getTime() - padding), new Date(maxEnd.getTime() + padding)]

    return scaleTime().domain(domain).range([0, zoomedWidth.value])
  })

  /**
   * Generate ticks for the X-axis.
   * The number of ticks scales with width and zoom level to prevent overcrowding.
   */
  const ticks = computed(() => {
    if (!timeScale.value) return []
    return timeScale.value.ticks(Math.max((width.value / 100) * uiStore.zoomLevel, 5))
  })

  return {
    totalHeight,
    zoomedWidth,
    timeScale,
    ticks
  }
}
