<script setup lang="ts">
import { ref, watch, onUnmounted } from 'vue'
import Dialog from 'primevue/dialog'
import { useEditor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import { Markdown } from 'tiptap-markdown'
import { wsManager, type WebSocketMessage } from '../composables/useWebSocket'

const props = defineProps<{
  visible: boolean
  category: string
  name: string
}>()

const emit = defineEmits<{
  'update:visible': [value: boolean]
}>()

type LoadState = 'idle' | 'loading' | 'ready' | 'error' | 'empty'

const loadState = ref<LoadState>('idle')
const loadError = ref('')
const images = ref<Array<{ mimeType: string; data: string }>>([])
const lightboxSrc = ref<string | null>(null)

// Keep editable:true so setContent works reliably; read-only appearance is via CSS
const editor = useEditor({
  extensions: [
    StarterKit,
    Markdown.configure({ html: false, transformPastedText: false }),
  ],
  content: '',
})

watch(() => props.visible, (v) => {
  if (v) {
    loadState.value = 'loading'
    loadError.value = ''
    images.value = []
    lightboxSrc.value = null
    editor.value?.commands.setContent('')
    wsManager.send({ type: 'read_output', category: props.category, name: props.name })
  } else {
    loadState.value = 'idle'
    images.value = []
    lightboxSrc.value = null
    editor.value?.commands.setContent('')
  }
})

const messageHandler = (msg: WebSocketMessage) => {
  if (msg.category !== props.category || msg.name !== props.name) return

  if (msg.type === 'output_data') {
    const hasAnalysis = !!msg.analysis
    const hasImages = Array.isArray(msg.images) && msg.images.length > 0
    if (!hasAnalysis && !hasImages) {
      loadState.value = 'empty'
      return
    }
    if (hasAnalysis) {
      editor.value?.commands.setContent(msg.analysis ?? '')
    }
    images.value = hasImages ? msg.images : []
    loadState.value = 'ready'
  } else if (msg.type === 'output_error') {
    loadError.value = msg.error ?? 'Failed to load output'
    loadState.value = 'error'
  }
}

wsManager.addHandler(messageHandler)
onUnmounted(() => wsManager.removeHandler(messageHandler))

function openLightbox(src: string) {
  lightboxSrc.value = src
}

function closeLightbox() {
  lightboxSrc.value = null
}
</script>

<template>
  <Dialog
    :visible="visible"
    :header="`Output — ${name}`"
    :modal="true"
    :closable="true"
    :style="{ width: '800px', maxWidth: '95vw' }"
    class="research-view-dialog"
    @update:visible="emit('update:visible', false)"
  >
    <div class="dialog-body">
      <div v-if="loadState === 'loading'" class="state-msg">
        <i class="pi pi-spin pi-spinner" /> Loading output…
      </div>
      <div v-else-if="loadState === 'error'" class="state-msg error">
        <i class="pi pi-exclamation-triangle" /> {{ loadError }}
      </div>
      <div v-else-if="loadState === 'empty'" class="state-msg muted">
        No output yet — run the script first.
      </div>
      <div v-else-if="loadState === 'ready'" class="content-scroll">
        <img
          v-for="(img, idx) in images"
          :key="idx"
          :src="`data:${img.mimeType};base64,${img.data}`"
          class="output-image"
          :alt="`Chart ${idx + 1}`"
          @click="openLightbox(`data:${img.mimeType};base64,${img.data}`)"
        />
        <EditorContent v-if="editor" :editor="editor" class="tiptap-viewer" />
      </div>
    </div>

    <!-- Lightbox overlay -->
    <Teleport to="body">
      <div v-if="lightboxSrc" class="lightbox-overlay" @click="closeLightbox">
        <img :src="lightboxSrc" class="lightbox-img" alt="Full size" @click.stop />
        <button class="lightbox-close" @click="closeLightbox">
          <i class="pi pi-times" />
        </button>
      </div>
    </Teleport>
  </Dialog>
</template>

<style scoped>
.dialog-body {
  min-height: 200px;
  display: flex;
  flex-direction: column;
}

.state-msg {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: #8a8a8a;
  font-size: 13px;
  min-height: 200px;
}

.state-msg.error { color: #e06c6c; }
.state-msg.muted { color: #555; }

.content-scroll {
  overflow-y: auto;
  max-height: 65vh;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-bottom: 4px;
}

.output-image {
  max-width: 100%;
  border-radius: 4px;
  border: 1px solid #2e2e2e;
  display: block;
  cursor: zoom-in;
  transition: border-color 0.15s;
}

.output-image:hover {
  border-color: #4a9eca;
}

.lightbox-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.88);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
  cursor: zoom-out;
}

.lightbox-img {
  max-width: 92vw;
  max-height: 92vh;
  border-radius: 4px;
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
  cursor: default;
}

.lightbox-close {
  position: absolute;
  top: 16px;
  right: 20px;
  background: rgba(30, 30, 30, 0.9);
  border: 1px solid #3d3d3d;
  color: #aaa;
  border-radius: 50%;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 13px;
}

.lightbox-close:hover {
  color: #fff;
  border-color: #666;
}
</style>

<style>
/* Global so TipTap's .ProseMirror gets styled */
.research-view-dialog .tiptap-viewer .ProseMirror {
  padding: 12px 14px;
  outline: none;
  font-size: 13px;
  line-height: 1.6;
  color: #dbdbdb;
  font-family: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
  /* Read-only appearance without disabling setContent */
  caret-color: transparent;
  cursor: default;
}

.research-view-dialog .tiptap-viewer .ProseMirror h1,
.research-view-dialog .tiptap-viewer .ProseMirror h2,
.research-view-dialog .tiptap-viewer .ProseMirror h3 {
  color: #eaeaea;
  margin: 0.8em 0 0.4em;
  font-size: 1.1em;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.research-view-dialog .tiptap-viewer .ProseMirror h1 { font-size: 1.3em; }
.research-view-dialog .tiptap-viewer .ProseMirror h2 { font-size: 1.15em; }

.research-view-dialog .tiptap-viewer .ProseMirror p { margin: 0 0 0.6em; }

.research-view-dialog .tiptap-viewer .ProseMirror strong {
  color: #eaeaea;
  font-weight: 700;
}

.research-view-dialog .tiptap-viewer .ProseMirror em {
  color: #bbb;
  font-style: italic;
}

.research-view-dialog .tiptap-viewer .ProseMirror code {
  background: #1a1a1a;
  border-radius: 3px;
  padding: 1px 5px;
  font-size: 0.9em;
  color: #89d4e0;
}

.research-view-dialog .tiptap-viewer .ProseMirror pre {
  background: #141414;
  border: 1px solid #2e2e2e;
  border-radius: 4px;
  padding: 10px 14px;
  overflow-x: auto;
  margin: 0.6em 0;
}

.research-view-dialog .tiptap-viewer .ProseMirror pre code {
  background: none;
  padding: 0;
  color: #dbdbdb;
}

.research-view-dialog .tiptap-viewer .ProseMirror ul,
.research-view-dialog .tiptap-viewer .ProseMirror ol {
  padding-left: 1.4em;
  margin: 0.4em 0;
}

.research-view-dialog .tiptap-viewer .ProseMirror blockquote {
  border-left: 3px solid #3d3d3d;
  margin: 0.6em 0;
  padding-left: 12px;
  color: #8a8a8a;
}

.research-view-dialog .tiptap-viewer .ProseMirror hr {
  border: none;
  border-top: 1px solid #2e2e2e;
  margin: 1em 0;
}
</style>
