<script setup lang="ts">
import { computed, onMounted, ref, onUnmounted, type ComponentPublicInstance } from 'vue'
import { useRoute } from 'vue-router'

import NodeInputsOutputs from './nodes/NodeInputsOutputs.vue'
import NodeSection from './nodes/NodeSection.vue'
import RawJsonViewer from './nodes/RawJsonViewer.vue'
import TraceNodeEmptyState from './nodes/TraceNodeEmptyState.vue'
import TraceNodeHeader from './nodes/TraceNodeHeader.vue'
import TraceVisualizer from './trace/TraceVisualizer.vue'
import JsonKeyValueDisplay from '@/components/nodes/JsonKeyValueDisplay.vue'
import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from '@/components/ui/resizable'
import { ScrollArea } from '@/components/ui/scroll-area'
import { useTraceValidation } from '@/composables/useTraceValidation'
import { useUiStore } from '@/stores/uiStore'
import type { SpanInputsOutputs } from '@/types/trace'
import { getUnknownRootFields } from '@/utils/unknownRootFields'

const route = useRoute()
const uiStore = useUiStore()
const selectedNode = computed(() => uiStore.selectedNode)

// check if inputs/outputs have displayable content
function hasContent(data: SpanInputsOutputs | undefined): boolean {
  if (!data) return false
  return Boolean(
    data.text ||
      data.messages?.length ||
      data.input !== undefined ||
      data.output !== undefined ||
      data.query ||
      data.checkedText ||
      data.error ||
      data.toolCalls?.length ||
      data.toolValidationRequests?.length ||
      data.memoryFragment ||
      data.sources?.length ||
      data.artifacts?.length ||
      data.detectedEntities?.length,
  )
}

const hasInputs = computed(() => hasContent(selectedNode.value?.inputs))
const hasOutputs = computed(() => hasContent(selectedNode.value?.outputs))
const hasAttributes = computed(() =>
  selectedNode.value?.attributes ? Object.keys(selectedNode.value.attributes).length > 0 : false,
)
const unknownRootFields = computed(() =>
  selectedNode.value
    ? getUnknownRootFields(selectedNode.value as unknown as Record<string, unknown>)
    : {},
)
const hasUnknownFields = computed(() => Object.keys(unknownRootFields.value).length > 0)

// validate trace/node existence with redirect + toast
useTraceValidation()

// scrollArea height measurement
const scrollAreaHeight = ref(0)
let resizeObserver: ResizeObserver | null = null

onMounted(() => {
  uiStore.selectTrace(route.params.traceId as string)
  uiStore.selectedNodeId = route.params.nodeId as string

  // set up ResizeObserver to track ScrollArea height
  resizeObserver = new ResizeObserver((entries) => {
    for (const entry of entries) {
      scrollAreaHeight.value = entry.contentRect.height
    }
  })
})

onUnmounted(() => {
  resizeObserver?.disconnect()
})

// watch for scrollAreaRef changes to attach observer
function onScrollAreaMounted(el: Element | ComponentPublicInstance | null) {
  if (el && resizeObserver) {
    const element = (el as ComponentPublicInstance)?.$el ?? el
    resizeObserver.observe(element)
    scrollAreaHeight.value = element.clientHeight
  }
}
</script>

<template>
  <ResizablePanelGroup direction="horizontal" auto-save-id="trace-view-panels" class="flex">
    <ResizablePanel :min-size="15" :default-size="30" class="flex flex-col">
      <TraceVisualizer />
    </ResizablePanel>

    <ResizableHandle with-handle />

    <ResizablePanel :min-size="33">
      <!-- Empty State -->
      <TraceNodeEmptyState v-if="!uiStore.selectedNodeId" />

      <!-- Node Inspector -->
      <div v-if="selectedNode" class="flex h-full flex-col">
        <ScrollArea :ref="onScrollAreaMounted" class="min-h-0 flex-1">
          <div class="flex min-h-full flex-col">
            <!-- Node Header -->
            <div class="sticky top-0 z-20 border-b bg-background/80 backdrop-blur-sm">
              <TraceNodeHeader :node="selectedNode" />
            </div>

            <!-- Empty State when no inputs or outputs -->
            <div
              v-if="!hasInputs && !hasOutputs && !hasAttributes && !hasUnknownFields"
              class="flex flex-1 items-center justify-center px-8 py-16 text-center text-muted-foreground"
            >
              <p>No inputs, outputs, or attributes available for this node.</p>
            </div>

            <!-- Unknown Root Fields -->
            <JsonKeyValueDisplay
              v-if="uiStore.nodeViewMode === 'formatted' && hasUnknownFields"
              :data="unknownRootFields"
              class="pl-4 pr-5 py-4"
            />
            <RawJsonViewer v-else-if="hasUnknownFields" :data="unknownRootFields" />

            <!-- Node Inputs -->
            <NodeSection v-if="hasInputs" title="Inputs" :data="selectedNode.inputs">
              <NodeInputsOutputs
                v-if="uiStore.nodeViewMode === 'formatted'"
                :data="selectedNode.inputs"
                class="p-2"
              />
              <RawJsonViewer v-else :data="selectedNode.inputs" />
            </NodeSection>

            <!-- Node Outputs -->
            <NodeSection v-if="hasOutputs" title="Outputs" :data="selectedNode.outputs">
              <NodeInputsOutputs
                v-if="uiStore.nodeViewMode === 'formatted'"
                :data="selectedNode.outputs"
                class="p-2"
              />
              <RawJsonViewer v-else :data="selectedNode.outputs" />
            </NodeSection>

            <!-- Node Attributes -->
            <NodeSection v-if="hasAttributes" title="Attributes" :data="selectedNode.attributes">
              <JsonKeyValueDisplay
                v-if="uiStore.nodeViewMode === 'formatted'"
                :data="selectedNode.attributes"
                class="pl-4 pr-5 py-4"
              />
              <RawJsonViewer v-else :data="selectedNode.attributes" />
            </NodeSection>
          </div>
        </ScrollArea>
      </div>
    </ResizablePanel>
  </ResizablePanelGroup>
</template>
